package com.pervasa.demo.re.impl.command;

import com.pervasa.demo.re.impl.command.Token.Kind;

public class Scanner {

	private int ch; /* ch contains next character */
	private String cmd;
	private char[] cmdAry;
	private static int charAryIndex = 0;

	public Scanner(String s) {
		charAryIndex = 0;
		this.cmd = s;
		System.out.println("Scanner: " + cmd);
		cmdAry = cmd.trim().toCharArray();
		getch();
	}
	
	/**
	 * Read the next char and update the current_line and ch_pos
	 */
	private void getch() {
		if (charAryIndex < cmdAry.length) {
			ch = cmdAry[charAryIndex];
			charAryIndex++;
		} else {
			ch = -1;
		}
	}

	private enum State {
		START, IDENT_PART, GOT_ZERO, DIGITS, EOF
	}

	private State state;

	/**
	 * Return one token after each call
	 * 
	 * @return
	 * @throws IOException
	 */
	public Token next() {
		state = State.START;

		StringBuffer sb = new StringBuffer();
		Token t = null;
		do {
			switch (state) {
			case START:
				switch (ch) {
				case -1: // end of file
					state = State.EOF;
					break;
				case ' ': // white space(s)
				case '\t':
				case '\f':
				case '\r':
				case '\n': // new line
					break;
				case '.': // Start case for separator
					t = new Token(Kind.DOT);
					break;
				case ';':
					t = new Token(Kind.SEMI);
					break;
				case ',':
					t = new Token(Kind.COMMA);
					break;
				case '(':
					t = new Token(Kind.LPAREN);
					break;
				case ')':
					t = new Token(Kind.RPAREN);
					break;
				case '[':
					t = new Token(Kind.LSQUARE);
					break;
				case ']':
					t = new Token(Kind.RSQUARE);
					break;
				// end case of separator

				// Start operator case
				case '=':
					t = new Token(Kind.BECOMES);
					break;
				case '+':
					t = new Token(Kind.PLUS);
					break;
				case '*':
					t = new Token(Kind.TIMES);
					break;
				// end of operation

//				case '0': // case zero
//					state = State.GOT_ZERO;
//					break;

				case '<':
					t = new Token(Kind.LANGLE);
					break;
				case '>':
					t = new Token(Kind.RANGLE);
					break;
				case '%':
					t = new Token(Kind.PERCENT);
					break;
				case '/':
					t = new Token(Kind.SLASH);
					break;
				case '-':
					t = new Token(Kind.MINUS);
					break;
				case ':':
					t = new Token(Kind.COLON);
					break;
					
				default:
					// System.out.println("default Get called");
					if (Character.isDigit(ch)) {
						sb.append((char) ch);
						// System.out.println("Start print sb  " + sb);
						state = State.DIGITS;
					} else if (Character.isJavaIdentifierStart(ch)) {
						sb.append((char) ch);
						state = State.IDENT_PART;
					} else {
						System.err.println("Illege Char");
					}
				}
				getch(); // this is for all cases in start statement
				break; // end of start

			case IDENT_PART: // IDENT_PART
				if (Character.isJavaIdentifierPart(ch)) {
					sb = sb.append((char) ch);
					state = State.IDENT_PART;
					getch();
				} else {
					Kind keyword = isKeyword(sb.toString());

					if (keyword.equals(Kind.IDENT)) {
						t = new IdentToken(sb.toString());
					} else {
						return new Token(keyword);
					}
					state = State.START;
				}
				break;

			case DIGITS: // DIGITS
				if (Character.isDigit(ch)) {
					sb = sb.append((char) ch);
					state = State.DIGITS;
					getch();
				} else {
					try {
						t = new IntLitToken(Integer.valueOf(sb.toString()));
					} catch (Exception e) {
						System.err.println("Wrong digits number");
						sb.delete(0, sb.length());
						state = State.START;
					}
				}
				break;

//			case GOT_ZERO: // GOT_ZERO
//				t = new IntLitToken(0);
//				state = State.START;
//				break;
			case EOF: // EOF
				t = new Token(Kind.EOF);
				break;
			default:
				assert false : "should not reach here";

			} // end of switch(state)
		} while (t == null); // loop terminates when a token has been created
		return t;
	}

	private static Kind isKeyword(String ident) {
		if (ident.equals("true")) {
			return Kind._TRUE;
		} else if (ident.equals("false")) {
			return Kind._FALSE;
		} else if (ident.toLowerCase().equals("set")) {
			return Kind.SET;
		} else if (ident.toLowerCase().equals("define")) {
			return Kind.DEFINE;
		} else if (ident.toLowerCase().equals("list")) {
			return Kind.LIST;
		} else if (ident.toLowerCase().equals("basic")) {
			return Kind.BASIC;
		} else if (ident.toLowerCase().equals("run")) {
			return Kind.RUN;
		} else if (ident.toLowerCase().equals("stop")) {
			return Kind.STOP;
		} else if (ident.toLowerCase().equals("load")) {
			return Kind.LOAD;
		} else if (ident.equals("event")) {
			return Kind.EVENT;
		} else if (ident.equals("condition")) {
			return Kind.CONDITION;
		} else if (ident.equals("action")) {
			return Kind.ACTION;
		} else if (ident.equals("rule")) {
			return Kind.RULE;
		} else if (ident.equals("clear")) {
			return Kind.CLEAR;
		} else if (ident.equals("nil")) {
			return Kind.NIL;
		} else if (ident.equals("inf")) {
			return Kind.INF;
		} else {
			return Kind.IDENT;
		}
	}

	public static void main(String[] args) {
		// String str = "TRUE FALSE  + *  SET " + "DEFINE LIST BASIC RUN STOP "
		// + "LOAD event condition action rule = ";

		String str = "LIST";
		
		
		Scanner scanner = new Scanner(str);

		String[] s1 = str.split(" ");

		for (int i = 0; i < s1.length; i++) {
			System.out.println(scanner.next());
			System.out.println(scanner.next());
		}
	}
}
